package com.asgard.crypto;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

import org.apache.commons.io.output.ByteArrayOutputStream;

import com.asgard.utils.SerializeUtils;

/**
 * 该类表示经过加密后的数据。
 */
public class EncryptedData {

    /**
     * IV
     */
    public final byte[] initialisationVector;
    /**
     * 加密后的字节数组
     */
    public final byte[] encryptedBytes;

    /**
     * 初始化加密后的数据。
     *
     * @param initialisationVector IV
     * @param encryptedBytes       加密后的字节数组
     */
    public EncryptedData(byte[] initialisationVector, byte[] encryptedBytes) {
        this.initialisationVector = Arrays.copyOf(initialisationVector, initialisationVector.length);
        this.encryptedBytes = Arrays.copyOf(encryptedBytes, encryptedBytes.length);
    }

    /**
     * 从输入流中加载加密后的数据。
     *
     * @param sin 输入流
     * @return 加密后的数据
     * @throws IOException 输入输出错误
     */
    public static EncryptedData readFromStream(InputStream sin) throws IOException {
        // 1. iv
        byte[] iv = SerializeUtils.readVarByteArray(sin);
        // 2. bytes
        byte[] bytes = SerializeUtils.readVarByteArray(sin);
        return new EncryptedData(iv, bytes);
    }

    /**
     * 将加密后的数据保存到输出流中。
     *
     * @param sout 输出流
     * @throws IOException 输入输出错误
     */
    public void writeToStream(OutputStream sout) throws IOException {
        // 1. iv
        SerializeUtils.writeVarByteArray(sout, initialisationVector);
        // 2. bytes
        SerializeUtils.writeVarByteArray(sout, encryptedBytes);
    }

    /**
     * 从字节数组中加载加密后的数据。
     *
     * @param bytes 字节数组
     * @return 加密后的数据
     */
    public static EncryptedData readFromByteArray(byte[] bytes) {
        try (ByteArrayInputStream bin = new ByteArrayInputStream(bytes)) {
            return EncryptedData.readFromStream(bin);
        } catch (IOException e) {
            // cannot happen
            return null;
        }
    }

    /**
     * 将加密后的数据保存到字节数组中。
     *
     * @return 字节数组
     */
    public byte[] writeToByteArray() {
        try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
            this.writeToStream(bout);
            return bout.toByteArray();
        } catch (IOException e) {
            // cannot happen
            return null;
        }
    }

}
